package slap.app;

import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jodd.util.ReflectUtil;
import jodd.util.StringUtil;
import jregex.Pattern;

import slap.annotation.Accept;
import slap.mvc.Action;
import slap.mvc.Controller;
import slap.mvc.HttpRequestController;
import slap.mvc.WebSocketHandler;
import slap.util.Asserts;

public class ActionParser {
	
	private static final Logger logger = LoggerFactory.getLogger(ActionParser.class);
	
	protected static Action initAction(Accept accept, Method actionMethod,
			Controller controller) throws InstantiationException,
			IllegalAccessException {
		Action action = new Action(actionMethod, controller).allow(accept.method());
		return action;
	}
	
	public static void parse(Method method, Application app){
		Class<? extends Controller> clazz = (Class<? extends Controller>) method.getDeclaringClass();
		Accept accept = method.getAnnotation(Accept.class);
		Asserts.notNull(accept);
		Asserts.notNull(app);
		
		if(ReflectUtil.isSubclass(clazz, HttpRequestController.class)){
			HttpRequestController controller = app.httpRequestControllers.get(clazz);
			if (controller == null) {
				try {
					controller = (HttpRequestController) clazz.newInstance();
					controller.application = app;
					app.httpRequestControllers.put(clazz, controller);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			parseHttpRequestAction(accept, method, controller, app);
		}else if(ReflectUtil.isSubclass(clazz, WebSocketHandler.class)){
			WebSocketHandler handler = app.webSocketHandlers.get(clazz.getName());
			if (handler == null) {
				try {
					handler = (WebSocketHandler) clazz.newInstance();
					app.webSocketHandlers.put(clazz.getName(), handler);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			
			if(accept == null){
				logger.error("Websocket controller [%s] parse error: no Accept specified.", clazz);
			}
			
			for (String direct : accept.value()) {
				if (StringUtil.isNotBlank(direct)) {
					app.webSocketHandlersMapping.put(direct, handler);
				}
			}
		}else{
			logger.debug("Ignore class [%s] : not a controller class.", clazz.getName());
			return;
		}
			
	}
	
	public static void parseHttpRequestAction(Accept accept, Method actionMethod, HttpRequestController controller, Application app) {
		
		logger.debug("Parsing controller action method : [{} in {}].",
				actionMethod.getName(), actionMethod.getDeclaringClass());
		boolean resolved = false;
		Action action = null;
		try {
			action = initAction(accept, actionMethod, controller);
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		
		for (String direct : accept.value()) {
			if (StringUtil.isNotBlank(direct)) {
				action.serveDirect(direct);
				if(app.directRouter.contains(action)){
					logger.error("Duplicate action route:[{} @ {}]", direct, actionMethod.toString());
				}else{
					app.directRouter.add(action);
				}
			}
		}

		for (String wildcard : accept.wildcard()) {
			if (StringUtil.isNotBlank(wildcard)) {
				action.serveWildcard(wildcard);
				if(app.wildcardRouter.contains(action)){
					logger.error("Duplicate action route:[{} @ {}]", wildcard, actionMethod.toString());
				}else{
					app.wildcardRouter.add(action);
				}
				
			}
		}

		for (String regex : accept.regex()) {
			if (StringUtil.isNotBlank(regex)) {
				action.serveRegex(new Pattern(regex));
				if(app.regexRouter.contains(action)){
					logger.error("Duplicate action route:[{} @ {}]", regex, actionMethod.toString());
				}else{
					app.regexRouter.add(action);
				}
			}
		}

	}
}
